////////////////////////////////////////
//	File Header
//	Author: Stephen Ringer | stephen.ringer@gmail.com
//	File: SAGAD3D_main.cpp
//
////////////////////////////////////////

////////////////////////////////////////
//	File Includes
#include "SAGA.h"           // return value macros
#include "SAGAD3D.h"        // class definitions
#include <wingdi.h>
//
////////////////////////////////////////

////////////////////////////////////////
//	External Global Variables
extern bool g_bLF;
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: UseWindow
//	Purpose:	
//		If in windowed mode this function lets you switch between
//		all available child windows given as parameter array to
//		the original Init() function call.
HRESULT SAGAD3D::UseWindow(UINT nHwnd) 
{
	LPDIRECT3DSURFACE9 pBack=NULL;

	if (!m_d3dpp.Windowed) return SAGA_OK;
	else if (nHwnd >= m_nNumhWnd) return SAGA_FAIL;
	//	Try to get the correct back buffer
	if (FAILED(m_pChain[nHwnd]->GetBackBuffer(0,
		D3DBACKBUFFER_TYPE_MONO,
		&pBack))) 
	{
		Log("Error: GetBackBuffer() failed in UseWindow()");
		return SAGA_FAIL;
	}

	//	Activate it for the Device 
	m_pDevice->SetRenderTarget(0, pBack);
	pBack->Release();
	m_nActivehWnd = nHwnd;
	return SAGA_OK;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: BeginRendering
//	Purpose:
//		Clear the scene and prepare device for retrieving geometry.
//		Parameters:
//			bool - clear pixel buffer?
//			bool - clear depth buffer?
//			bool - clear stencil buffer?
HRESULT SAGAD3D::BeginRendering(bool bClearPixel, bool bClearDepth, bool bClearStencil) 
{
	DWORD dw=0;
	//	Check if there is anything to clear
	if (bClearPixel || bClearDepth || bClearStencil) 
	{
	   if (bClearPixel)	dw |= D3DCLEAR_TARGET;
	   if (bClearDepth)	dw |= D3DCLEAR_ZBUFFER;

	   if (bClearStencil && m_bStencil)
		   dw |= D3DCLEAR_STENCIL;

	   if (FAILED(m_pDevice->Clear(0, NULL, dw, m_ClearColor, 1.0f, 0))) 
	   {
		   this->Log("SAGA_FAIL: Clear() \n");
		   return SAGA_FAIL;
	   }
	}

	if (FAILED(m_pDevice->BeginScene()))
	   return SAGA_FAIL;

	m_bIsSceneRunning = true;
	return SAGA_OK;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: Clear
//	Purpose:
//		Just clear the scene. Only call this when Scene has already begun.
//		Parameters:
//			bool - clear pixel buffer?
//			bool - clear depth buffer?
//			bool - clear stencil buffer?
HRESULT SAGAD3D::Clear(bool bClearPixel, bool bClearDepth, bool bClearStencil) 
{
	DWORD dw=0;

	if (bClearPixel)   dw |= D3DCLEAR_TARGET;
	if (bClearDepth)   dw |= D3DCLEAR_ZBUFFER;

	if (bClearStencil && m_bStencil)
	  dw |= D3DCLEAR_STENCIL;

	if (m_bIsSceneRunning)
	  m_pDevice->EndScene();

	if (FAILED(m_pDevice->Clear(0, NULL, dw, m_ClearColor, 1.0f, 0))) 
	{
		this->Log("ZFX_FAIL: Clear() \n");
		return SAGA_FAIL;
	}

	if (m_bIsSceneRunning)
		m_pDevice->BeginScene();

	return SAGA_OK;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: EndRendering
//	Purpose:
//		End of render operations and flip scene to front buffer
void SAGAD3D::EndRendering() 
{
	//	Flush all vertex buffers
	if (FAILED(m_pVertexMan->ForcedFlushAll()))
		Log("Error: ForceFlushAll() from EndRendering() failed");

	m_pDevice->EndScene();

	if (m_d3dpp.Windowed && (m_nNumhWnd > 0) ) 
	{
		if (FAILED(m_pChain[m_nActivehWnd]->Present(NULL, NULL, NULL, NULL, 0)))
			Log("Error: Chain->Present() from EndRendering() failed");
	}
	else 
	{
		if (FAILED(m_pDevice->Present(NULL, NULL, NULL, NULL)))
			Log("Error: Deviec->Present() from EndRendering() failed");
	}

	m_bIsSceneRunning = false;
}
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: SetClearColor
//	Purpose:
//		Change the color of the screen clearing operation.
//		Parameters:
//			float - red
//	        float - green
//	        float - blue
void SAGAD3D::SetClearColor(float fRed, float fGreen, float fBlue) 
{ m_ClearColor = D3DCOLOR_COLORVALUE(fRed, fGreen, fBlue, 1.0f); }
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: SetWorldTransform
//	Purpose:
//		Set the world transformation matrix transforming any objects
//		rendered till setting changes again. This will force a flush
//		of all vertex buffers.
//		Parameters:
//			SAGAMatrix* - new matrix or NULL for no transform
void SAGAD3D::SetWorldTransform(const SAGAMatrix *mWorld) 
{
	//	Last chance check
	m_pVertexMan->ForcedFlushAll();

	//	Set class attribute 'world matrix'
	if (!mWorld) 
	{
		SAGAMatrix m; m.Identity();
		memcpy(&m_mWorld, &m, sizeof(D3DMATRIX)); 
	}
	else memcpy(&m_mWorld, mWorld, sizeof(D3DMATRIX)); 

	//	Recalculate connected values
	CalcWorldViewProjMatrix();

	//	If shaders are possible set transform as constant to device
	if (m_bCanDoShaders) 
	{
		SAGAMatrix mTranspose;
		mTranspose.TransposeOf(*(SAGAMatrix*)&m_mWorldViewProj);
		m_pDevice->SetVertexShaderConstantF(0, (float*)&mTranspose, 4);
	}

	m_pDevice->SetTransform(D3DTS_WORLD, &m_mWorld);
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: SetAbientLight
//	Purpose:
//		Set ambient light level to given values. If shaders are used the
//		ambient color is stored in C4.
void SAGAD3D::SetAmbientLight(float fRed, float fGreen, float fBlue) 
{
	//	Last chance check
	m_pVertexMan->ForcedFlushAll();

	int nRed   = (int)(fRed * 255.0f);
	int nGreen = (int)(fGreen * 255.0f);
	int nBlue  = (int)(fBlue * 255.0f);

	if (m_bCanDoShaders) 
	{
		//	Default setting to use as diffuse vertex color
		float fCol[4] = { fRed, fGreen, fBlue, 1.0f };
		m_pDevice->SetVertexShaderConstantF(4, fCol, 1);
	}

	m_pDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(nRed, nGreen, nBlue));
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: CreateFont
//	Purpose:
//		Creates a D3DXFont object from a GDI font the caller submitted
//		and returns its ID to the caller for later use.
HRESULT SAGAD3D::CreateFont(const char *chType, int nWeight, bool bItalic,
						   bool bUnderline, bool bStrike, DWORD dwSize, UINT *pID) 
{
	HRESULT hr;
	HDC hDC;
	int nHeight;

	if (!pID) return SAGA_INVALIDPARAM;

	hDC = GetDC( NULL );
	nHeight = -MulDiv(dwSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
	ReleaseDC(NULL, hDC);

	m_pFont = (LPD3DXFONT*)realloc(m_pFont, sizeof(LPD3DXFONT)*(m_nNumFonts+1));

	// build D3DX font from GDI font
	hr = D3DXCreateFont( m_pDevice, // Direct3D device
		nHeight, nHeight/2, // font height and width
		nWeight, // thickness, 0=default, 700=bold
		1, // mipmap levels
		bItalic, // italic style
		DEFAULT_CHARSET, // character set
		OUT_DEFAULT_PRECIS, // precision
		DEFAULT_QUALITY, // quality
		DEFAULT_PITCH | FF_DONTCARE, // pitch and family
		"Arial", // font face (=font type)
		&m_pFont[m_nNumFonts]); // address for new font

	if (SUCCEEDED(hr)) 
	{
		(*pID) = m_nNumFonts;
		m_nNumFonts++;
		return SAGA_OK;
	}
	else return SAGA_FAIL;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: DrawText
//	Purpose:
//		Draws text using the previously created font object.
HRESULT SAGAD3D::DrawText(UINT nID, int x, int y, UCHAR r, UCHAR g, UCHAR b, char *ch, ...) 
{
	RECT rc = { x, y, 0, 0 };
	char cch[1024];
	char *pArgs;

	//	Put variables into the string
	pArgs = (char*) &ch + sizeof(ch);
	vsprintf(cch, ch, pArgs);
	if (nID >= m_nNumFonts) return SAGA_INVALIDPARAM;
	//	Calculate actual size of the text
	m_pFont[nID]->DrawText(NULL, cch, -1, &rc, DT_SINGLELINE | DT_CALCRECT, 0);
	//	Now draw the text 
	m_pFont[nID]->DrawText(NULL, cch, -1, &rc, DT_SINGLELINE, D3DCOLOR_ARGB(255,r,g,b));

	return SAGA_OK;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: FadeScreen
//	Purpose:
//		Fade the screen to given color by blending a semi-transparent
//		quad over the whole backbuffer.
//		Parameters:
//			float - red
//			float - green
//			float - blue
//			float - alpha
void SAGAD3D::FadeScreen(float fR, float fG, float fB, float fA) 
{
	SAGAENGINEMODE OldMode;
	LVERTEX       v[4];
	bool          bChanged=false;
	WORD          wI[6] = { 0, 1, 2, 0, 3, 1 };
	DWORD         dwColor = D3DCOLOR_COLORVALUE(fR,fG,fB,fA);

	if (m_bIsSceneRunning) m_pDevice->EndScene();
	//	Actual skin gets invalid
	SetActiveSkinID(MAX_ID);

	v[0].x     = (float)m_dwWidth;   
	v[0].y     = 0.0f;
	v[0].z     = 1.0f;
	v[0].tu    = 1.0f;
	v[0].tv    = 0.0f;
	v[0].Color = dwColor;

	v[1].x     = 0.0f;   
	v[1].y     = (float)m_dwHeight;
	v[1].z     = 1.0f;
	v[1].tu    = 0.0f;
	v[1].tv    = 1.0f;
	v[1].Color = dwColor;

	v[2].x     = 0.0f;   
	v[2].y     = 0.0f;
	v[2].z     = 1.0f;
	v[2].tu    = 0.0f;
	v[2].tv    = 0.0f;
	v[2].Color = dwColor;

	v[3].x     = (float)m_dwWidth;   
	v[3].y     = (float)m_dwHeight;
	v[3].z     = 1.0f;
	v[3].tu    = 1.0f;
	v[3].tv    = 1.0f;
	v[3].Color = dwColor;

	SetWorldTransform(NULL);
	SetView3D(SAGAVector(1,0,0), SAGAVector(0,1,0), SAGAVector(0,0,1), SAGAVector(0,0,0));

	UseShaders(false);
	m_pDevice->SetTexture(0, NULL);
	//   m_pDevice->SetStreamSource(0, NULL, 0, sizeof(LVERTEX));
	//   m_pDevice->SetIndices(NULL);
	m_pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
	m_pDevice->SetFVF(FVF_LVERTEX);

	//	Check projection mode
	if (m_Mode != EMD_TWOD) 
	{
		bChanged = true;
		OldMode  = m_Mode;
		SetMode(EMD_TWOD, m_nStage);
	}

	//	Set material with alpha value
	D3DMATERIAL9 mtrl;
	memset(&mtrl, 0, sizeof(D3DMATERIAL9));
	mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
	mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
	mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f;
	mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
	m_pDevice->SetMaterial(&mtrl);

	//	Activate alpha blending using alpha from material
	m_pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

	m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	m_pDevice->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA);
	m_pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	m_pDevice->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_MODULATE);
	m_pDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	m_pDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

	//	Don't need Zbuffer
	SetDepthBufferMode(RS_DEPTH_NONE);

	//	Don't need cool shading, skip to gain speed
	m_pDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);

	m_pDevice->BeginScene();

	if (FAILED(m_pDevice->DrawIndexedPrimitiveUP(	D3DPT_TRIANGLELIST, 0, 4, 2, wI, D3DFMT_INDEX16, v, 
													sizeof(LVERTEX))))
		Log("error: DrawPrimitiveUP()");

	m_pDevice->EndScene();

	if (bChanged) SetMode(OldMode, m_nStage);

	m_pDevice->SetMaterial(&m_StdMtrl);

	//	Restore old D3D states
	m_pDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2, D3DTA_CURRENT);
	m_pDevice->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	m_pDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
	m_pDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);

	SetDepthBufferMode(RS_DEPTH_READWRITE);

	if (m_bIsSceneRunning)
		m_pDevice->BeginScene();
}
//
////////////////////////////////////////